﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using xplang.run;
using xplang.abstractions;
using System.Diagnostics;

namespace xplang.expression
{

  public abstract class InvokeMethod : Statement
  {

    public InvokeMethod(ClassMethod method, Expression[] parameters)
      : base(method.result.type)
    {
      this.method = method;
      this.parameters = parameters;
    }

    public readonly ClassMethod method;
    public readonly Expression[] parameters;

    public Value[] makeHeap(Value _thisobj, Value[] heap, ref FlowState state)
    {
      int n = method.parameters.Length;
      Value[] mh = method.makeHeap(_thisobj, null);
      for (int i = 1; i <= n; i++)
      {
        Value p = parameters[i - 1].doIt(heap, ref state);
        if (state != FlowState.Normal)
          return null;
        if (method.parameters[i - 1].byRef())
          mh[i] = p;
        else
          mh[i] = p.clone();
      }
      return mh;
    }

    public override bool isBlock()
    {
      return false;
    }
  }

  [DebuggerDisplay("invokeInternal {method}")]
  public class InvokeInternalMethod : InvokeMethod
  {

    public InvokeInternalMethod(ClassMethod method, Expression[] parameters)
      : base(method, parameters)
    {
    }

    public override Value doIt(Value[] heap, ref FlowState state)
    {
      int n = method.parameters.Length;
      Value _thisobj = heap[0];
      if (state != FlowState.Normal)
        return null;
      Value[] mh = makeHeap(_thisobj, heap, ref state);
      if (state != FlowState.Normal)
        return null;
      return method.invokeIt(mh, ref state);
    }
  }

  [DebuggerDisplay("invokeExternal {method}")]
  public class InvokeExternalMethod : InvokeMethod
  {

    public InvokeExternalMethod(Expression _this, ClassMethod method, Expression[] parameters)
      : base(method, parameters)
    {
      this._this = _this;
    }

    public readonly Expression _this;
    protected Value _thisobj;

    public override Value doIt(Value[] heap, ref FlowState state)
    {
      int n = method.parameters.Length;
      _thisobj = _this.doIt(heap, ref state);
      if (state != FlowState.Normal)
        return null;
      Value[] mh = makeHeap(_thisobj, heap, ref state);
      if (state != FlowState.Normal)
        return null;
      return method.invokeIt(mh, ref state);
    }
  }

  public class PreOperatorInvoke : InvokeExternalMethod
  {
    public PreOperatorInvoke(Expression _this, ClassMethod method) :
      base(_this, method, null)
    {
    }

    public override Value doIt(Value[] heap, ref FlowState state)
    {
      Value v = base.doIt(heap, ref state);
      if (state != FlowState.Normal)
        return null;
      _thisobj.pointer = v.pointer;
      return heap[0].clone();
    }
  }

  public class PostOperatorInvoke : InvokeExternalMethod
  {
    public PostOperatorInvoke(Expression _this, ClassMethod method) :
      base(_this, method, null)
    {
    }

    public override Value doIt(Value[] heap, ref FlowState state)
    {
      Value r = heap[0].clone();
      Value v = base.doIt(heap, ref state);
      if (state != FlowState.Normal)
        return null;
      _thisobj.pointer = v.pointer;
      return r;
    }
  }

  public class SelfAssignOperator : InvokeExternalMethod
  {
    public SelfAssignOperator(Expression _this, ClassMethod method, Expression value) :
      base(_this, method, new Expression[] { value })
    {
    }

    public override Value doIt(Value[] heap, ref FlowState state)
    {
      Value v = base.doIt(heap, ref state);
      if (state != FlowState.Normal)
        return null;
      _thisobj.pointer = v.pointer;
      return v;
    }
  }
}
